﻿#include <learn/topo/topo_builder.h>
#include <learn/topo/topo_explorer.h>

using namespace xi;
using namespace xi::topo;

Topo_Vertex *Topo_Builder::create_vertex()
{
    return new Topo_Vertex{};
}

Topo_Edge *Topo_Builder::create_edge(Topo_Vertex *v1, Topo_Vertex *v2)
{
    // 禁止创建自环边
    ASSERT_INFO(v1 != nullptr && v2 != nullptr, "v1 and v2 should not be null");
    ASSERT_INFO(v1 != v2, "v1 and v2 should not be the same");

    // 创建新的边
    EHandle e = new Topo_Edge;

    e->m_start = v1;
    e->m_end = v2;

    // 创建 coedge
    Topo_Coedge *co1 = new Topo_Coedge;
    Topo_Coedge *co2 = new Topo_Coedge;

    // 建立拓扑连接
    e->m_coedge = co1;
    co1->edge() = e;
    co2->edge() = e;
    co1->twin() = co2;
    co2->twin() = co1;

    // 设置顶点的 edge
    v1->edges().push_back(e);
    v2->edges().push_back(e);

    return e;
}

Topo_Wire *Topo_Builder::create_wire(const std::vector<Topo_Coedge *> &es)
{
    Topo_Explorer explorer;
    ASSERT_INFO(es.size() > 0, "es should not be empty");
    for (auto e : es)
        ASSERT_INFO(e != nullptr, "e should not be null");

    // 检测是否有重复的边
    auto checkDuplicate = [&]() -> bool {
        std::set<Topo_Coedge *> eset;
        for (auto e : es)
            eset.insert(e);
        return eset.size() == es.size();
    };

    ASSERT_INFO(checkDuplicate(), "es should not contain duplicate elements");

    // 直接创建 loop，不需要创建边
    auto loop = new Topo_Wire;
    loop->coedge() = es.front();

    // 获得一个起点
    auto e0 = es.front();
    e0->prev() = es.back();
    es.back()->next() = e0;
    e0->wire() = loop;
    ASSERT_INFO(explorer.start_vertex(e0) == explorer.end_vertex(e0->prev()), "Coedge should be connected");

    // 需要建立半边之间的联系
    for (std::size_t i = 1; i < es.size(); i++)
    {
        es[i]->prev() = es[i - 1];
        es[i - 1]->next() = es[i];
        es[i]->wire() = loop;
        ASSERT_INFO(explorer.start_vertex(es[i]) == explorer.end_vertex(es[i - 1]), "Coedge should be connected");
    }

    return loop;
}

Topo_Face *Topo_Builder::create_face(const std::vector<Topo_Coedge *> &es)
{
    auto loop = create_wire(es);
    loop->face() = new Topo_Face;
    loop->face()->wire() = loop;
    return loop->face();
}

Topo_Face *Topo_Builder::create_face(Topo_Wire *wire)
{
    if (wire == nullptr)
        return nullptr;

    wire->face() = new Topo_Face;
    wire->face()->wire() = wire;
    return wire->face();
}

Topo_Shell *Topo_Builder::create_shell(Topo_Wire *wire)
{
    if (wire == nullptr)
        return nullptr;

    auto shell = new Topo_Shell;
    shell->m_shapes = wire;
    return shell;
}

Topo_Shell *Topo_Builder::create_shell(Topo_Face *face)
{
    if (face == nullptr)
        return nullptr;

    auto shell = new Topo_Shell;
    shell->m_shapes = face;
    return shell;
}

Topo_Shell *Topo_Builder::create_shell(const std::vector<Topo_Shell *> &shells)
{
    if (shells.empty())
        return nullptr;

    auto shell = new Topo_Shell;
    shell->m_shapes = shells;
    return shell;
}

bool Topo_Builder::remove_vertex(Topo_Vertex *v)
{
    if (v == nullptr)
        return false;

    delete v;
    return true;
}

bool Topo_Builder::remove_edge(Topo_Edge *e)
{
    if (e == nullptr)
        return false;

    delete e;
    return true;
}

bool Topo_Builder::remove_coedge(Topo_Coedge *c)
{
    if (c == nullptr)
        return false;

    delete c;
    return true;
}

bool Topo_Builder::remove_wire(Topo_Wire *w)
{
    if (w == nullptr)
        return false;

    delete w;
    return true;
}

bool Topo_Builder::remove_face(Topo_Face *f)
{
    if (f == nullptr)
        return false;

    delete f;
    return true;
}

bool Topo_Builder::remove_shell(Topo_Shell *s)
{
    if (s == nullptr)
        return false;

    delete s;
    return true;
}
